This is a local mirror of the Altera FreeCore Library. It is no longer supported.

FreeCore Library
Instantiating Functions in AHDL
by
Rune Baeverrud

Instantiating Functions in AHDL

In this tutorial we will take a closer look at different ways of instantiating functions. There are basically 2 methods you could use:

With in-line instances, there are also 2 types of port associations:

I will be using the following two example functions for the demonstrations. The first function is non-parameterized 4-to-16 line decoder. The second function is parameterized n-to-2^n line decoder. (By the way - do you see the beauty of parameterized design?)

  Old-Style Non-Parameterized:

SUBDESIGN dec
(
  address [3..0] : INPUT = GND;
  decode [15..0] : OUTPUT;
)

BEGIN
  TABLE
    address[] => decode[];
    0         => B"0000000000000001";
    1         => B"0000000000000010";
    2         => B"0000000000000100";
    3         => B"0000000000001000";
    4         => B"0000000000010000";
    5         => B"0000000000100000";
    6         => B"0000000001000000";
    7         => B"0000000010000000";
    8         => B"0000000100000000";
    9         => B"0000001000000000";
    10        => B"0000010000000000";
    11        => B"0000100000000000";
    12        => B"0001000000000000";
    13        => B"0010000000000000";
    14        => B"0100000000000000";
    15        => B"1000000000000000";
  END TABLE;
END;

  New-Style Parameterized:

PARAMETERS
(
  WIDTH = 4
);

CONSTANT TOTAL_BITS = 2^WIDTH;

SUBDESIGN parm_dec
(
  address [(WIDTH-1)..0]     : INPUT = GND;
  decode [(TOTAL_BITS-1)..0] : OUTPUT;
)

BEGIN
  FOR i IN 0 TO (TOTAL_BITS-1) GENERATE
    IF address[] == i THEN 
      decode[i] = VCC;
    END IF;
  END GENERATE;
END;

Note that the address[] input of both functions has a default value of GND. This means that if we don't connect these inputs externally, they will default to GND.

The function prototypes for the above functions will be created by MAX+PLUS II when you choose File->Create Default Include File from the pull-down menu. This is how the function prototypes look like:

FUNCTION dec (address[3..0])
    RETURNS (decode[15..0]);
FUNCTION parm_dec (address[(width) - (1)..0])
    WITH (WIDTH)
    RETURNS (decode[((2) ^ (width)) - (1)..0]);

You must use the INCLUDE statement to include the function prototypes into your design file before you can use the functions.

In-line Instantiation of Functions

Let's have a look at an example AHDL file using in-line instantiations of the functions above. This example looks very chaotic (and it is!), but please don't be scared off just yet! Read on...

INCLUDE "dec";
INCLUDE "parm_dec";

SUBDESIGN top_1
(
  a[3..0]       : INPUT;
  b[1..0][1..0] : INPUT;

  apple[15..0]  : OUTPUT;
  banana[7..0]  : OUTPUT;
  nut           : OUTPUT;
  pear, peach   : OUTPUT;
)

BEGIN
  (pear, peach) = dec (a[])
                  RETURNS (.decode15, .decode7);

  apple[]       = dec (, , a2, a0);

  nut           = parm_dec (VCC, b[1][])
                  WITH (WIDTH = 3)
                  RETURNS (.decode0);

  banana[3..0]  = parm_dec ( .address[2..1] = (VCC, b[1][1]), 
                             .(address3, address0) = (a3,) )
                  WITH (WIDTH = 4)
                  RETURNS (.decode15, .decode[2..0]);

  banana[7..4]  = dec ( .address[3..2] = b[0][], 
                        .address[1..0] = (, a1) )
                  RETURNS (.decode[1..0], .decode[1..0]);
END;

The example above show a mixture of positional and named port associations. You can not mix positional and named port associations within the same statement. Here is a summary of the functions using different types of port associations:

Positional port associations Named port associations
  • (pear, peach)
  • apple[]
  • nut
  • banana[3..0]
  • banana[7..4]

With positional port association, you use a string of bits placed in any order you want as the function input. You have to make sure that the number of bits you provide in the function input is the same as number of bits in the function prototype. If an input of a function has a default value, you don't have to specify a value for that input. This is where you see the lonely comma sign (',').

With named port association, you name the ports you want to use and assign the values of your choice. You don't have to give every port a value if the ports have default values.

By default, the function you instantiate returns the same number of bits as defined in the function prototype. If you want to return a lower number of bits, or if you want to return the same bits twice (like in the banana[7..4] function), or if you want to return the bits in another order, you must use the RETURNS statement to name and state the order of the returned bits.

You may also use lonely comma signs on the left side if you want to discard return values. For example, you could replace the line

apple[] = dec(, , a2, a0);

with

(apple[15..4],,,,) = dec(, , a2, a0);

if you want to discard the 4 least significant bits returned from the dec function. In this case - apple[3..0] remains unassigned.

As you can see, the only difference between using a parameterized function as compared to a non-parameterized function, is the use of the WITH ( ) clause.

Variable Instantiation of Functions

The following example uses variable instances of the dec and parm_dec functions. The syntax is very straigth forward and very easily readable. This is the syntax style I normally use in my own designs, as I find it more complete and more easily maintainable.

INCLUDE "dec";
INCLUDE "parm_dec";

SUBDESIGN top_2
(
  a[3..0]       : INPUT;
  b[1..0][1..0] : INPUT;

  apple[15..0]  : OUTPUT;
  nut           : OUTPUT;
)

VARIABLE
  orange, raisin : dec;
  parm_orange    : parm_dec WITH (WIDTH = 4);

BEGIN
  orange.(address3, address0) = GND;
  orange.address[2..1]        = (VCC, a1);
  apple[3..0]                 = (orange.decode[12..11],
                                 orange.decode15, VCC);

  parm_orange.address[] = b[][];
  apple[15..4]          = parm_orange.(decode13,
                                       decode[15..5]);

  raisin.address[] = (VCC, GND);
  nut              = raisin.decode15;
END;

Did you wonder about the line raisin.address[] = (VCC, GND)? The left side of this equation requires 4 bits, on the right side there are only 2 bits. What happens is that the 2 bits on the right side are replicated, so that the bit string assigned to raisin.address[] is (VCC, GND, VCC, GND).


Last updated 08 Feb 2001 12:10